//
//  WCCommunication.swift
//  RemoteService
//
//  Created by Longxiang Guo (QXQ4633) on 2021/5/23.
//

import Foundation
import Combine

struct WCCommand {
    
    enum ID: String {
        case colorTime
        case vehicleInfo
        case remoteCommand
        case vehicleImageFile
        case watchFile
    }
    
    enum State {
        case initial
        case processing
        case success
        case failure
    }
}

struct WCCommandInfo {
   //TODO:
}

protocol WCCommunicationProtocol {
    
    func publisher<T: Codable, B: Codable>(id: WCCommand.ID, body: B) -> AnyPublisher<T?, Error>
    
    func subscriber<T: Codable>(id: WCCommand.ID, subscribe: Subscribers.Sink<T?, Error>)
}

class WCCommunication: WCCommunicationProtocol {
    
    let service: WCServiceProtocol!
    
    private  var tables = [WCCommand.ID : PassthroughSubject<Codable,Error>]()
    
    init(service: WCServiceProtocol) {
        self.service = service
    }
    
    func activate(){
        service.startSession()
    }
   
    func publisher<T: Codable, B: Codable>( id: WCCommand.ID, body: B) -> AnyPublisher<T, Error>{
        guard let message = warpperMessage(message: body) else {
            return  Empty().eraseToAnyPublisher()
        }
       
        switch id {
        case .colorTime,
             .vehicleInfo,
             .remoteCommand:

            service.sendMessage.send(WatchMessage(message: message, withReply: {[unowned self] (reply) in
                if let message = unwarpperMessage(message: reply, type: T.self) {
                    getTabPublisher(id: id).send(message)
                }
            }))
          
        case .watchFile,
             .vehicleImageFile:
            service.sendFile.send(WatchFile(url: URL(fileURLWithPath: ""), metadata: ["CREATION_DATE": Date().description]))
        }
        return getTabPublisher(id: id)
            .map({ $0 as! T})
            .eraseToAnyPublisher()
    }
    
    func subscriber<T: Codable>(id: WCCommand.ID, subscribe: Subscribers.Sink<T?, Error>){
        getTabPublisher(id: id)
            .map({ $0 as? T})
            .subscribe(subscribe)
    }
     
    
   private func warpperMessage<T: Codable>(message: T) -> [String: Any]? {
        guard let messageData = try? JSONEncoder().encode(message) else {
            WatchLog("warpperMessage to data error:\(message)")
            return nil
        }
        guard let json = try? JSONSerialization.jsonObject(with: messageData, options: .fragmentsAllowed) as? [String: Any] else {
            WatchLog("warpperMessage to json error :\(message)")
            return nil
        }
        return json
    }
    
    private  func unwarpperMessage<T: Codable>(message: [String: Any], type: T.Type) -> T? {
        guard let jsonData = try? JSONSerialization.data(withJSONObject: message, options: .fragmentsAllowed) else {
            WatchLog("unwarpperMessage to data error:\(message) ")
            return nil
        }
        guard let msg = try? JSONDecoder().decode(T.self, from: jsonData) else {
            WatchLog("unwarpperMessage to object error:\(message) ")
            return nil
        }
        return msg
        
    }
    private func getTabPublisher(id: WCCommand.ID) -> PassthroughSubject<Codable, Error>{
        if (tables.keys.contains(id)){
            return tables[id]!
        }
        let subject = PassthroughSubject<Codable, Error>()
        tables[id]! = subject
        return subject
    }
}
